import { useCallback, useEffect, useMemo, useRef, useState, forwardRef, useImperativeHandle } from 'react';
import { getDvaApp, history } from 'umi';
import { Tooltip } from 'antd';
import classnames from 'classnames';
import { Anchor, Header, Icon, Loading } from 'gantd';
import { isEmpty as _isEmpty } from 'lodash';

import type { ReactElement, ReactNode } from 'react';
import type { GantAnchorProps } from 'gantd/lib/anchor';

import { Empty, Title } from '@/components/common';
import { getBizSummary } from '@/services/api';
import { addRecentHistory, BLOCK_HEADER_HEIGHT, getCurrentPath, setDocumentTitle } from '@/utils/utils';

import ItemRender from './ItemRender';
import TitleRender from './TitleRender';
import {
  contextMenuCache,
  filterContextMenu,
  getAnchorsAndRenderContents,
  layoutCache,
  pareseContextMenu,
} from './utils';
import { getContextMenuAPI } from './service';

import styles from './index.less';

export interface ListItem {
  id: string;
  title: string | ReactElement;
  complete?: boolean;
  isInvalid?: boolean;
  [key: string]: any;
}

export interface AnchorListItem extends ListItem {
  height: number; // 渲染内容对应初始高度
  icon?: string | ReactNode; // 对应图标
  content?: ReactNode; // 渲染的内容
  component?: ReactNode | any; // 渲染的组件
  modelPath?: string; //
  countKey?: string; // 加载统计数量的key
  count?: string | number | ReactNode; // 在Tag上显示的统计值
  itemKey?: string; // 在上下文菜单中的一个配置属性，自定义锚点可忽律
  resizable: boolean; // 是否可改变渲染项容器大小，建议配置的是content设置为true
  minHeight?: number; // 渲染内容最小高度
  maxHeight?: number; // 渲染内容最大高度
  recordRenderingStatus?: boolean; // 是否记录渲染状态
  autoHeight?: boolean; // 是否开启自适应高度，建议渲染内容为表单可以开启该属性
}

export type ContentItem = Pick<
  AnchorListItem,
  | 'id'
  | 'title'
  | 'content'
  | 'coponent'
  | 'height'
  | 'resizable'
  | 'minHeight'
  | 'maxHeight'
  | 'recordRenderingStatus'
  | 'autoHeight'
>;

export type MenuFilter = Record<string, string | number | boolean>;

export type RefreshContextMenuProps = {
  menufilter?: MenuFilter; // 对上下文锚点项过滤显示条件
  showInvalidMenu?: boolean; // 显示无效锚点，被过滤menu不显示内容，在锚点中显示但不可点击
  isEmpty?: boolean; // 当前业务对象是否不存在
};

// 刷新上下文菜单列表状态
export type RefreshContextMenu = (props?: RefreshContextMenuProps) => Promise<void>;

// 动态属性
export interface DynamicCmpProps {
  title: string; // 菜单名称
  height: number; // 配置的高度
  refreshContextMenu: RefreshContextMenu; // 刷新菜单列表的回调
  refreshCountKeys: (countKeys: Record<string, any>) => void; // 刷新配置的统计值显示
}

export interface ContextAnchorProps extends Omit<GantAnchorProps, 'getContainer'> {
  isReady: boolean; // 业务层的数据是否准备好，例如preffixList、suffixList、menufilter、customCountKeys
  title: string; // 标题
  category?: string | number; // 上下文记录类型
  lazyload?: boolean; // 懒加载模式
  cacheKey: string; // 记录状态的缓存id
  showInvalidMenu?: boolean; //显示无效锚点，被过滤menu不显示内容，在锚点中显示但不可点击
  preffixList?: AnchorListItem[]; // 在上下菜单前面的自定义锚点列表
  suffixList?: AnchorListItem[]; // 在上下菜单后面的自定义锚点列表
  dynamicCmpProps?: any; // 透传给动态加载组件的额外属性
  layout?: 'horizontal' | 'vertical';
  minHeight?: number | string; // 最小高度
  headerProps?: any;
  threshold?: number; // 预加载的阀值
  menufilter?: MenuFilter; // 对上下文锚点项过滤显示条件
  customCountKeys?: Record<string, number | string | ReactNode>; // 对上下文锚点项自定义count数量显示，这里的key需与上下文菜单中配置的itemKey的值一样
  defaultHeight?: number; // 统一配置每个渲染项的初始渲染高度，如果在上下文菜单中没有配置高度，将会使用这个高度作为初始渲染高度
  defaultMinHeight?: number; // 统一配置每个渲染项的最小高度，如果在上下文菜单中没有配置最小高度，将会使用这个高度作为可调整的最小高度
  defaultRecordRenderingStatus?: boolean; // 统一配置每个渲染项是否记录渲染状态，如果在上下文菜单中没有配置记录渲染状态，将会使用根据这个值来决定是否记录渲染状态
  stars?: { name: string; pathname: string }[]; // 我的收藏列表
  isEmpty?: boolean; // 如果当前业务对象不存在，需设置该属性为true
  MAIN_CONFIG?: any; //系统配置
  titleExtra?: any; // title后面额外的容器
  showBackBtn?: boolean; // 是否显示返回的按钮
  showTitle?: boolean; // 是否显示标题
  helpDocCategory?: string; //wordpress帮助文档的跳转目录，详情页需要
  customFilterContextMenu?: any; // 自定义过滤菜单
}

const INIT_OBJECT = {};
const INIT_ARRAY: any[] = [];
//挂载全部编辑的ref
const wholeEditRef = { current: {} };
/**
 * 上下文锚点组件
 */
const ContextAnchor = forwardRef((props: ContextAnchorProps, ref: any) => {
  const {
    isReady = false,
    title,
    category,
    lazyload = true,
    cacheKey,
    showInvalidMenu = false,
    preffixList = INIT_ARRAY,
    suffixList = INIT_ARRAY,
    dynamicCmpProps = INIT_OBJECT,
    layout = 'vertical',
    minHeight: minHeightProp = 400,
    headerProps = INIT_OBJECT,
    threshold = 50,
    menufilter = INIT_OBJECT,
    customCountKeys: outCustomCountKeys = INIT_OBJECT,
    defaultHeight = 200,
    defaultMinHeight = 100,
    defaultRecordRenderingStatus = false,
    stars,
    isEmpty: isEmptyProp = false,
    MAIN_CONFIG = INIT_OBJECT,
    titleExtra,
    showBackBtn = true,
    showTitle = true,
    helpDocCategory,
    customFilterContextMenu,
    ...resProps
  } = props;

  const containerRef = useRef<any>(null);
  const { dispatch } = getDvaApp()._store;

  const { showTaskBar = false } = MAIN_CONFIG;
  const [isLoad, setIsLoad] = useState(!category && isReady);
  const [currentLayout, setCurrentLayout] = useState(() => {
    return layoutCache.get(cacheKey) || layout;
  });
  const [customCountKeys, setCustomCountKeys] = useState(outCustomCountKeys);
  const [showList, setShowList] = useState<any[]>([]);
  const [showContents, setShowContents] = useState<any[]>([]);
  const [isEmpty, setIsEmpty] = useState(isEmptyProp);
  // 缓存所有的菜单列表
  const allContextListRef = useRef<AnchorListItem[]>([]);

  const [hasAuth, setHasAuth] = useState(true);

  const menuFilterRef = useRef(menufilter);
  const preffixListRef = useRef(preffixList);
  const suffixListListRef = useRef(suffixList);
  const refreshContextMenuRef = useRef(() => {});
  const isEmptyRef = useRef(isEmptyProp);

  const currentPath = getCurrentPath();
  const hasStar = useCallback(
    (pathname: string) => {
      return !!_.find(stars, (o) => o.pathname == pathname);
    },
    [stars],
  );
  const hasStared = hasStar(currentPath);

  useMemo(() => {
    menuFilterRef.current = menufilter;
  }, [menufilter]);

  useMemo(() => {
    preffixListRef.current = preffixList;
  }, [preffixList]);

  useMemo(() => {
    suffixListListRef.current = suffixList;
  }, [suffixList]);

  useMemo(() => {
    isEmptyRef.current = isEmptyProp;
  }, [isEmptyProp]);

  const [minHeight, allHeight] = useMemo(() => {
    const diff = 5;
    const getHeight = (height: string | number, diff: number) => {
      return typeof height === 'number' ? height - diff : `calc(${height} - ${diff}px)`;
    };
    const height = '100%';
    const minHeight = getHeight(height, BLOCK_HEADER_HEIGHT + diff);
    const allHeight = getHeight(height, diff);
    return [showTitle ? minHeight : allHeight, allHeight];
  }, [showTitle]);

  const headerTitle = useMemo(() => {
    return (
      <>
        {showTaskBar || !showBackBtn ? (
          <i className="margin5" />
        ) : (
          <Icon
            type="left"
            className="margin5"
            onClick={() => {
              history.goBack();
            }}
          />
        )}
        <Title title={title} showHelpIcon helpDocCategory={helpDocCategory} />
        {titleExtra}
      </>
    );
  }, [title, titleExtra]);

  const showAnchorList = useMemo(() => {
    return showList.map((item) => {
      const { itemKey, ...res } = item;
      const extra: any = {};
      if (itemKey && customCountKeys[itemKey]) {
        extra.count = customCountKeys[itemKey];
      }
      return {
        ...res,
        title: <TitleRender {...item} {...extra} />,
      };
    });
  }, [showList, customCountKeys]);

  const content = useMemo(() => {
    return showContents.map((item, index) => {
      return (
        <ItemRender
          key={item.id}
          params={{
            ...dynamicCmpProps,
            wholeEditRef,
            refreshContextMenu: refreshContextMenuRef.current,
            refreshCountKeys: (newCountKeys: any) => {
              setCustomCountKeys((pre) => ({
                ...pre,
                ...newCountKeys,
              }));
            },
          }}
          cacheKey={cacheKey}
          threshold={threshold}
          lazyload={lazyload}
          isLast={showContents.length - 1 === index}
          isFirst={index === 0}
          defaultHeight={defaultHeight}
          defaultMinHeight={defaultMinHeight}
          defaultRecordRenderingStatus={defaultRecordRenderingStatus}
          {...item}
          wrapperContainerRef={containerRef}
        />
      );
    });
  }, [showContents, dynamicCmpProps]);

  const onLayoutChange = useCallback((layout) => {
    setCurrentLayout(layout);
    layoutCache.set(cacheKey, layout);
  }, []);

  // 获取统计信息
  const getCount = useCallback(async (anchorList: AnchorListItem[]) => {
    const topic = anchorList.filter((item) => item.countKey).map((id, countKey) => ({ id, topic: countKey }));
    if (_isEmpty(topic)) return anchorList;
    try {
      const res: any = await getBizSummary({ data: { topic } });
      return anchorList.map((item) => {
        const { id } = item;
        return {
          ...item,
          count: res[id],
        };
      });
    } catch (error) {
      console.error('加载上下文菜单-获取统计信息出错', error);
    }
    return anchorList;
  }, []);

  // 获取上下菜单列表，配置动态加载组件
  const getContextMenu: RefreshContextMenu = useCallback(
    async (props = {}) => {
      const { menufilter = {}, showInvalidMenu: showInvalidMenuParam, isEmpty = false } = props;

      if (isEmpty) {
        setIsEmpty(true);
        return;
      }

      // 这样业务层每次只需修改目标项
      const currentMenuFilter = {
        ...(menuFilterRef.current || {}),
        ...menufilter,
      };
      menuFilterRef.current = currentMenuFilter;
      const currentPreffixList = preffixListRef.current;
      const currentSuffixList = suffixListListRef.current;
      const currentShowInvalidMenu = showInvalidMenuParam || showInvalidMenu;

      try {
        const cacheList = contextMenuCache.get(category);
        let res: any[] = cacheList;
        if (!Array.isArray(cacheList) || cacheList.length === 0) {
          res = await getContextMenuAPI({ contextCategory: category });
          res.sort((a, b) => a.seqNum - b.seqNum);
          contextMenuCache.set(category, res);
        }
        if (_isEmpty(res) && _isEmpty(currentPreffixList) && _isEmpty(currentSuffixList)) {
          setIsLoad(true);
          setHasAuth(false);
          return;
        }
        // 解析
        const contextList: AnchorListItem[] = pareseContextMenu(res);

        // 所有渲染项列表
        let allContextList: AnchorListItem[] = allContextListRef.current;

        // 表明是首次加载，否则是内部调用刷新
        if (allContextList.length === 0) {
          allContextList = [...currentPreffixList, ...contextList, ...currentSuffixList];
          allContextListRef.current = allContextList;
        }

        // 原过滤不满足
        if (customFilterContextMenu) {
          allContextList = await customFilterContextMenu(allContextList);
        }

        // 过滤
        const newContextList = filterContextMenu(allContextList, currentMenuFilter);

        // 获取统计数据
        const resContextList = await getCount(newContextList);
        // 获取渲染锚点列表和对应内容列表
        const [showList, showContents] = getAnchorsAndRenderContents(
          allContextList,
          resContextList,
          currentShowInvalidMenu,
        );
        setShowList(showList);
        setShowContents(showContents);
      } catch (error) {
        console.error('加载上下文菜单出错', error);
      }
      setIsLoad(true);
    },
    [customFilterContextMenu],
  );

  refreshContextMenuRef.current = getContextMenu;

  // 收藏
  const actionStar = () => {
    const pathname = getCurrentPath();
    const has = hasStar(pathname);
    const newStars = _.cloneDeep(stars);
    if (!has) {
      newStars &&
        newStars.unshift({
          name: title,
          pathname,
        });
    } else {
      newStars &&
        newStars.map((star: object, index: number) => {
          if (star.pathname === pathname) {
            newStars.splice(index, 1);
          }
        });
    }
    dispatch({
      type: 'menu/updateStars',
      payload: {
        data: newStars,
      },
    });
  };

  //暴露方法
  useImperativeHandle(
    ref,
    () => ({
      //获取所有动态加载的组件
      getWholeEditRef: () => {
        return wholeEditRef;
      },
    }),
    [],
  );

  //重置全部编辑Ref
  useEffect(() => {
    wholeEditRef.current = {};
  }, []);

  //loading处理
  useEffect(() => {
    if (isReady && isEmptyRef.current) {
      setIsLoad(true);
      setIsEmpty(true);
      return;
    }
    isReady && category && getContextMenu();
  }, [isReady, getContextMenu]);

  //更新最进浏览和document title
  useEffect(() => {
    // 这里在一些场景下不需要更改标题
    if (!showBackBtn || !showTitle) return;
    title && addRecentHistory(title);
    setDocumentTitle(title);
    return () => setDocumentTitle('');
  }, [title, showBackBtn]);

  useEffect(() => {
    setCustomCountKeys((pre) => {
      return { ...pre, ...outCustomCountKeys };
    });
  }, [outCustomCountKeys]);

  return (
    <div className={classnames('ant-card-bordered', styles['context-anchor-wrapper'])}>
      {!isLoad && <Loading height={'100%'} index={2} />}
      {isLoad &&
        (!isEmpty ? (
          hasAuth ? (
            <>
              {showTitle && (
                <Header
                  title={
                    <>
                      {headerTitle}
                      <Tooltip title={hasStared ? tr('取消收藏') : tr('收藏')}>
                        <span
                          className="gant-margin-h-10"
                          style={{
                            fontSize: 16,
                            color: hasStared ? 'var(--warning-color)' : 'var(--text-color)',
                            cursor: 'pointer',
                          }}
                          onClick={actionStar}
                        >
                          <Icon type="star" theme={hasStared ? 'filled' : ''} />
                        </span>
                      </Tooltip>
                    </>
                  }
                  className={styles.header}
                  {...headerProps}
                />
              )}
              <div ref={containerRef} style={{ height: minHeight, overflow: 'hidden auto' }}>
                <Anchor
                  {...resProps}
                  list={showAnchorList}
                  fixedTop={0}
                  layout={currentLayout}
                  content={content}
                  onLayoutChange={onLayoutChange}
                  getContainer={() => containerRef.current || window}
                  className="gant-layout"
                />
              </div>
            </>
          ) : (
            <Empty title={tr('没有任何上下文菜单的权限')} height={allHeight} showBackBtn={showBackBtn} />
          )
        ) : (
          <Empty title={tr('当前业务对象不存在或已被删除')} height={allHeight} showBackBtn={showBackBtn} />
        ))}
    </div>
  );
});

ContextAnchor.displayName = 'ContextAnchor';

export default ContextAnchor;
